home *** CD-ROM | disk | FTP | other *** search
- /* Buffered input/output. */
-
- #include <errno.h>
- #include <fcntl.h>
- #include <sys/types.h>
- #include "alloc.h"
- #include "fail.h"
- #include "io.h"
- #include "memory.h"
- #include "misc.h"
- #include "mlvalues.h"
- #include "signals.h"
- #include "sys.h"
- #ifdef HAS_UI
- #include "ui.h"
- #endif
-
- /* Common functions. */
-
- /* Improvement suggested by Doug Currie:
- memoize std_in, std_out and std_err.
- Used also in function flush_stdouterr.
-
- As usual, std_channel[0] = stdin,
- std_channel[1] = stdout,
- std_channel[2] = stderr.
- */
-
- static struct channel *std_channel[3] = {NULL, NULL, NULL};
-
- struct channel * open_descr(fd)
- int fd;
- {
- struct channel * channel;
-
- if( (unsigned)fd < 3 && std_channel[fd] != NULL )
- return std_channel[fd];
- channel = (struct channel *) stat_alloc(sizeof(struct channel));
- channel->fd = fd;
- channel->offset = 0;
- channel->curr = channel->max = channel->buff;
- channel->end = channel->buff + IO_BUFFER_SIZE;
- if( (unsigned)fd < 3 )
- std_channel[fd] = channel;
- return channel;
- }
-
- value open_descriptor(fd) /* ML */
- value fd;
- {
- return (value) open_descr(Int_val(fd));
- }
-
- value channel_descriptor(channel) /* ML */
- struct channel * channel;
- {
- return Val_long(channel->fd);
- }
-
- value channel_size(channel) /* ML */
- struct channel * channel;
- {
- long end;
-
- end = lseek(channel->fd, 0, 2);
- if (end == -1) sys_error(NULL);
- if (lseek(channel->fd, channel->offset, 0) != channel->offset)
- sys_error(NULL);
- return Val_long(end);
- }
-
- /* Output */
-
- static void really_write(fd, p, n)
- int fd;
- char * p;
- int n;
- {
- int retcode;
- while (n > 0) {
- #ifdef HAS_UI
- retcode = ui_write(fd, p, n);
- #else
- #ifdef EINTR
- do { retcode = write(fd, p, n); } while (retcode == -1 && errno == EINTR);
- #else
- retcode = write(fd, p, n);
- #endif
- #endif
- if (retcode == -1) sys_error(NULL);
- p += retcode;
- n -= retcode;
- }
- }
-
- value flush(channel) /* ML */
- struct channel * channel;
- {
- int n;
- n = channel->max - channel->buff;
- if (n > 0) {
- really_write(channel->fd, channel->buff, n);
- channel->offset += n;
- channel->curr = channel->buff;
- channel->max = channel->buff;
- }
- return Atom(0);
- }
-
- void flush_stdouterr(void)
- {
- if (std_channel[1]) flush(std_channel[1]);
- if (std_channel[2]) flush(std_channel[2]);
- }
-
- value output_char(channel, ch) /* ML */
- struct channel * channel;
- value ch;
- {
- putch(channel, Long_val(ch));
- return Atom(0);
- }
-
- void putword(channel, w)
- struct channel * channel;
- uint32 w;
- {
- putch(channel, w >> 24);
- putch(channel, w >> 16);
- putch(channel, w >> 8);
- putch(channel, w);
- }
-
- value output_int(channel, w) /* ML */
- struct channel * channel;
- value w;
- {
- putword(channel, Long_val(w));
- return Atom(0);
- }
-
- void putblock(channel, p, n)
- struct channel * channel;
- char * p;
- unsigned n;
- {
- unsigned m;
-
- m = channel->end - channel->curr;
- if (channel->curr == channel->buff && n >= m) {
- really_write(channel->fd, p, n);
- channel->offset += n;
- } else if (n <= m) {
- bcopy(p, channel->curr, n);
- channel->curr += n;
- if (channel->curr > channel->max) channel->max = channel->curr;
- } else {
- bcopy(p, channel->curr, m);
- p += m;
- n -= m;
- m = channel->end - channel->buff;
- really_write(channel->fd, channel->buff, m);
- channel->offset += m;
- if (n <= m) {
- bcopy(p, channel->buff, n);
- channel->curr = channel->max = channel->buff + n;
- } else {
- really_write(channel->fd, p, n);
- channel->offset += n;
- channel->curr = channel->max = channel->buff;
- }
- }
- }
-
- value output(channel, buff, start, length) /* ML */
- value channel, buff, start, length;
- {
- putblock((struct channel *) channel,
- &Byte(buff, Long_val(start)),
- (unsigned) Long_val(length));
- return Atom(0);
- }
-
- value seek_out(channel, pos) /* ML */
- struct channel * channel;
- value pos;
- {
- long dest;
-
- dest = Long_val(pos);
- if (dest >= channel->offset &&
- dest <= channel->offset + channel->max - channel->buff) {
- channel->curr = channel->buff + dest - channel->offset;
- } else {
- flush(channel);
- if (lseek(channel->fd, dest, 0) != dest) sys_error(NULL);
- channel->offset = dest;
- }
- return Atom(0);
- }
-
- value pos_out(channel) /* ML */
- struct channel * channel;
- {
- return Val_long(channel->offset + channel->curr - channel->buff);
- }
-
- value close_out(channel) /* ML */
- struct channel * channel;
- {
- if ((unsigned)(channel->fd) >= 3)
- {
- flush(channel);
- close(channel->fd);
- stat_free((char *) channel);
- }
- return Atom(0);
- }
-
- /* Input */
-
- static int really_read(fd, p, n)
- int fd;
- char * p;
- unsigned n;
- {
- int retcode;
-
- enter_blocking_section();
- #ifdef HAS_UI
- retcode = ui_read(fd, p, n);
- #else
- #ifdef MSDOS
- retcode = msdos_read(fd, p, n);
- #else
- #ifdef EINTR
- do { retcode = read(fd, p, n); } while (retcode == -1 && errno == EINTR);
- #else
- retcode = read(fd, p, n);
- #endif
- #endif
- #endif
- leave_blocking_section();
- if (retcode == -1) sys_error(NULL);
- return retcode;
- }
-
- unsigned char refill(channel)
- struct channel * channel;
- {
- int n;
-
- n = really_read(channel->fd, channel->buff, IO_BUFFER_SIZE);
- if (n == 0) mlraise(Atom(END_OF_FILE_EXN));
- channel->offset += n;
- channel->max = channel->buff + n;
- channel->curr = channel->buff + 1;
- return (unsigned char)(channel->buff[0]);
- }
-
- value input_char(channel) /* ML */
- struct channel * channel;
- {
- unsigned char c;
- c = getch(channel);
- return Val_long(c);
- }
-
- uint32 getword(channel)
- struct channel * channel;
- {
- int i;
- uint32 res;
-
- res = 0;
- for(i = 0; i < 4; i++) {
- res = (res << 8) + getch(channel);
- }
- return res;
- }
-
- value input_int(channel) /* ML */
- struct channel * channel;
- {
- long i;
- i = getword(channel);
- #ifdef SIXTYFOUR
- i = (i << 32) >> 32; /* Force sign extension */
- #endif
- return Val_long(i);
- }
-
- unsigned getblock(channel, p, n)
- struct channel * channel;
- char * p;
- unsigned n;
- {
- unsigned m, l;
-
- m = channel->max - channel->curr;
- if (n <= m) {
- bcopy(channel->curr, p, n);
- channel->curr += n;
- return n;
- } else if (m > 0) {
- bcopy(channel->curr, p, m);
- channel->curr += m;
- return m;
- } else if (n < IO_BUFFER_SIZE) {
- l = really_read(channel->fd, channel->buff, IO_BUFFER_SIZE);
- channel->offset += l;
- channel->max = channel->buff + l;
- if (n > l) n = l;
- bcopy(channel->buff, p, n);
- channel->curr = channel->buff + n;
- return n;
- } else {
- channel->curr = channel->buff;
- channel->max = channel->buff;
- l = really_read(channel->fd, p, n);
- channel->offset += l;
- return l;
- }
- }
-
- int really_getblock(chan, p, n)
- struct channel * chan;
- char * p;
- unsigned long n;
- {
- unsigned r;
- while (n > 0) {
- r = getblock(chan, p, (unsigned) n);
- if (r == 0) return 0;
- p += r;
- n -= r;
- }
- return 1;
- }
-
- value input(channel, buff, start, length) /* ML */
- value channel, buff, start, length;
- {
- return Val_long(getblock((struct channel *) channel,
- &Byte(buff, Long_val(start)),
- (unsigned) Long_val(length)));
- }
-
- value seek_in(channel, pos) /* ML */
- struct channel * channel;
- value pos;
- {
- long dest;
-
- dest = Long_val(pos);
- if (dest >= channel->offset - (channel->max - channel->buff) &&
- dest <= channel->offset) {
- channel->curr = channel->max - (channel->offset - dest);
- } else {
- if (lseek(channel->fd, dest, 0) != dest) sys_error(NULL);
- channel->offset = dest;
- channel->curr = channel->max = channel->buff;
- }
- return Atom(0);
- }
-
- value pos_in(channel) /* ML */
- struct channel * channel;
- {
- return Val_long(channel->offset - (channel->max - channel->curr));
- }
-
- value close_in(channel) /* ML */
- struct channel * channel;
- {
- close(channel->fd);
- stat_free((char *) channel);
- return Atom(0);
- }
-
- value input_scan_line(channel) /* ML */
- struct channel * channel;
- {
- char * p;
- int n;
-
- p = channel->curr;
- do {
- if (p >= channel->max) {
- /* No more characters available in the buffer */
- if (channel->curr > channel->buff) {
- /* Try to make some room in the buffer by shifting the unread
- portion at the beginning */
- bcopy(channel->curr, channel->buff, channel->max - channel->curr);
- n = channel->curr - channel->buff;
- channel->curr -= n;
- channel->max -= n;
- p -= n;
- }
- if (channel->max >= channel->end) {
- /* Buffer is full, no room to read more characters from the input.
- Return the number of characters in the buffer, with negative
- sign to indicate that no newline was encountered. */
- return Val_long(-(channel->max - channel->curr));
- }
- /* Fill the buffer as much as possible */
- n = really_read(channel->fd, channel->max, channel->end - channel->max);
- if (n == 0) {
- /* End-of-file encountered. Return the number of characters in the
- buffer, with negative sign since we haven't encountered
- a newline. */
- return Val_long(-(channel->max - channel->curr));
- }
- channel->offset += n;
- channel->max += n;
- }
- } while (*p++ != '\n');
- /* Found a newline. Return the length of the line, newline included. */
- return Val_long(p - channel->curr);
- }
-